package com.atguigu.tingshu.payment.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.util.HttpClient;
import com.atguigu.tingshu.dto.PaymentDto;
import com.atguigu.tingshu.model.payment.PaymentInfo;
import com.atguigu.tingshu.payment.factory.PaywayMark;
import com.atguigu.tingshu.payment.mapper.PaymentInfoMapper;
import com.atguigu.tingshu.payment.service.PaymentInfoService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.esotericsoftware.minlog.Log;
import com.github.wxpay.sdk.WXPayUtil;
import lombok.SneakyThrows;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.CompletableFuture;

/**
 * 微信支付的实现类
 */
@Service
@PaywayMark(payway = "1101")
@SuppressWarnings({"unchecked", "rawtypes"})
public class WxPaymentInfoServiceImpl implements PaymentInfoService {
  @Value("${appid}")
  private String appId;

  @Value("${partner}")
  private String partner;

  @Value("${partnerkey}")
  private String partnerkey;

  @Value("${notifyUrl}")
  private String notifyUrl;

  @Autowired
  private PaymentInfoMapper paymentInfoMapper;

  @Autowired
  private RabbitTemplate rabbitTemplate;

  @Autowired
  private RedissonClient redissonClient;
  /**
   * 获取微信的支付地址
   * @param paymentDto
   * @return
   */
  @SneakyThrows
  @Override
  public String getPayAddress(PaymentDto paymentDto) {
    //申请支付地址的接口
    String url = "https://api.mch.weixin.qq.com/pay/unifiedorder";
    //请求对象初始化
    HttpClient httpClient = new HttpClient(url);
    //请求参数
    Map<String, String> mapParams = new HashMap<>();
    mapParams.put("appid", appId); // 公众账号Id
    mapParams.put("mch_id", partner); // 商户号
    mapParams.put("nonce_str", WXPayUtil.generateNonceStr()); // 随机字符串
    mapParams.put("body", paymentDto.getBody()); // 商品描述
    mapParams.put("out_trade_no", paymentDto.getOrderNo()); // 订单号
    mapParams.put("total_fee", "1");// 金额 单位: 分
    mapParams.put("spbill_create_ip", "192.168.200.1");
    mapParams.put("notify_url", notifyUrl);// 异步通知地址
    mapParams.put("trade_type", "NATIVE");

    //附加数据
    Map<String, String> attach = new HashMap<>();
    attach.put("exchange", paymentDto.getExchange());
    attach.put("routingKey", paymentDto.getRoutingKey());
    // 封装数据
    mapParams.put("attach", JSONObject.toJSONString(attach));

    // 将map转化为xml
    String params = WXPayUtil.generateSignedXml(mapParams, partnerkey);
    // 将参数放入http请求中
    httpClient.setXmlParam(params);
    // 设置为https加密请求
    httpClient.setHttps(true);

    // 发起post请求
    httpClient.post();

    // 获取响应结果: xml格式的字符串
    String contentXmlString = httpClient.getContent();
    // 解析结果 将xml转化为map
    Map<String, String> result = WXPayUtil.xmlToMap(contentXmlString);
    // 如果成功获取到微信返回的支付链接 则新增paymentInfo到数据库中
    if (result.get("return_code").equals("SUCCESS") && result.get("result_code").equals("SUCCESS")){
      // 这个地方不用上锁 因为微信每次请求间隔至少15s 并且收到响应后就不会再通知
      // 异步保存本次交易的流水记录(paymentInfo)到数据库中
      CompletableFuture.runAsync(()->{
        savePaymentInfo(paymentDto);
      });
      // 返回结果
      return result.get("code_url");

    }
    // 失败抛异常
    throw new GuiguException(201, JSONObject.toJSONString(result));
  }


  /**
   * 异步保存本次交易的流水记录
   * @param paymentDto
   */
  private void savePaymentInfo(PaymentDto paymentDto) {
    //初始化
    PaymentInfo paymentInfo = new PaymentInfo();
    //用户
    paymentInfo.setUserId(paymentDto.getUserId());
    //订单 1301-订单
    paymentInfo.setPaymentType("1301");
    //订单号
    paymentInfo.setOrderNo(paymentDto.getOrderNo());
    //渠道
    paymentInfo.setPayWay(paymentDto.getPayway());
    //金额 单位: 元  分 / 100 = 元
    paymentInfo.setAmount(new BigDecimal(paymentDto.getMoney()).divide(new BigDecimal(100)));
    //描述
    paymentInfo.setContent(paymentDto.getBody());
    //状态
    paymentInfo.setPaymentStatus("1401");
    //保存到数据库中
    paymentInfoMapper.insert(paymentInfo);
  }

  /**
   * 主动查询支付结果
   * @param orderNo
   * @return
   */
  @SneakyThrows
  @Override
  public Boolean getPayResult(String orderNo) {
    //查询支付结果
    String url = "https://api.mch.weixin.qq.com/pay/orderquery";
    //请求对象初始化
    HttpClient httpClient = new HttpClient(url);
    //请求参数
    Map<String, String> mapParams = new HashMap<>();
    mapParams.put("appid", appId);
    mapParams.put("mch_id", partner);
    mapParams.put("nonce_str", WXPayUtil.generateNonceStr());
    mapParams.put("out_trade_no", orderNo);
    //将map转换为xml
    String params = WXPayUtil.generateSignedXml(mapParams, partnerkey);
    //存放参数
    httpClient.setXmlParam(params);
    //发起post请求
    httpClient.setHttps(true);
    httpClient.post();
    //获取响应的结果xml格式的字符串
    String contentXmlString = httpClient.getContent();
    //解析结果
    Map<String, String> result = WXPayUtil.xmlToMap(contentXmlString);
    //通信标识字段判断
    if(result.get("return_code").equals("SUCCESS") &&
            result.get("result_code").equals("SUCCESS")
            && result.get("trade_state").equals("SUCCESS")){
      //修改交易流水
      this.afterPaid(result);
      //返回
      return true;
    }
    //失败抛异常
    // throw new GuiguException(201, JSONObject.toJSONString(result));
    return false;
  }

  /**
   * 支付完成以后的后续处理
   * @param notifyResult
   */
  public void afterPaid(Map<String, String> notifyResult) {
    // 获取订单号
    String orderNo = notifyResult.get("out_trade_no");
    // 获取分布式锁
    RLock lock = redissonClient.getLock("Payment_Info_Lock" + orderNo);
    // 加锁直至成功为止: 每一笔通知结果都要单独判断
    lock.lock();

    try {
      // 根据订单号查询订单的支付信息
      PaymentInfo paymentInfo = paymentInfoMapper.selectOne(new LambdaQueryWrapper<PaymentInfo>()
                                                                    .eq(PaymentInfo::getOrderNo, orderNo));
      // 支付信息为空直接返回
      if (paymentInfo == null){
        return;
      }
      // 判断支付状态
      String paymentStatus = paymentInfo.getPaymentStatus();
      // 获取交易流水号
      String transactionId = notifyResult.get("transaction_id");

      // 如果订单未支付:1401
      if (paymentStatus.equals("1401")){
        // 获取附加数据
        String attachString = notifyResult.get("attach");
        // 反序列化
        Map<String, String> attach = JSONObject.parseObject(attachString, Map.class);
        // 获取交换机
        String exchange = attach.get("exchange");
        // 获取routingKey
        String routingKey = attach.get("routingKey");
        // 通知 订单模块去修改自己的订单的状态
        rabbitTemplate.convertAndSend(exchange, routingKey, orderNo);
        // 修改支付信息的状态: 1402-已支付, 并且补全数据
        paymentInfo.setPaymentStatus("1402");
        paymentInfo.setOutTradeNo(transactionId);
        paymentInfo.setCallbackTime(new Date());
        paymentInfo.setCallbackContent(JSONObject.toJSONString(notifyResult));
        paymentInfoMapper.updateById(paymentInfo);
      } else {
        // 订单已支付:1402 如果该笔订单在其他渠道已被支付 则通知退款
        if (paymentInfo.getOutTradeNo().equals(transactionId)){
          // 本次退款
          Log.error("触发调用微信支付的退款");
          // .........代码省略...................
        }
      }
    } catch (Exception e) {
      throw e;
    } finally {
      lock.unlock(); // 释放锁
    }

  }

}
